#ifndef UNIAST_UNIASTVISITOR_H
#define UNIAST_UNIASTVISITOR_H
#include "UniASTParser.h"
#include "UniASTParserBaseVisitor.h"
#include "tree/TerminalNode.h"
#include <any>
#include <cctype>
#include <iostream>
#include <string>

namespace UniAST
{
class Emitter
{
  private:
    std::ostream &stream;
    int tabCount;
    bool isNewLine;

  public:
    Emitter() = delete;
    Emitter(Emitter &) = delete;
    Emitter(Emitter &&) = delete;
    Emitter(std::ostream &stream)
        : stream(stream), tabCount(0), isNewLine(true)
    {
    }
    void tab()
    {
        tabCount++;
    }

    void shiftTab()
    {
        tabCount--;
        if (tabCount < 0)
        {
            tabCount = 0;
        }
    }

    void newLine()
    {
        this->stream << std::endl;
        isNewLine = true;
    }

    std::ostream &operator<<(std::string s)
    {
        if (this->isNewLine)
        {
            for (int i = 0; i <= (this->tabCount - 1); i++)
            {
                this->stream << '\t';
            }
            this->isNewLine = false;
        }
        this->stream << s;
        return this->stream;
    }
};

struct ExtraDecl
{
    enum DECL_TYPE
    {
        CONSTRUCTOR,
        TYPE,
        NODES,
        STRING_LITERAL
    };
    DECL_TYPE type;
    std::string content;
    std::vector<ExtraDecl> args;
};

class Visitor : public UniASTParserBaseVisitor
{
  private:
    Emitter lexerEmitter;
    Emitter parserEmitter;
    Emitter listenerHeaderEmitter;
    Emitter listenerCppEmitter;
    Emitter cmakeEmitter;
    Emitter driverEmitter;
    std::string grammarName;
    std::vector<std::string> ctxHasExtraDecl;
    void _emitTree(antlr4::tree::ParseTree *ctx, Emitter &emitter,
                   std::vector<std::string> nodesList = {})
    {
        for (auto child : ctx->children)
        {
            if (dynamic_cast<antlr4::tree::TerminalNode *>(child))
            {
                emitter << child->getText() << " ";
            }
            else
            {
                if (auto terminalsuffixed = dynamic_cast<
                        UniASTParser::Suffixed_terminaldefContext *>(
                        child))
                {
                    if (!terminalsuffixed->atom_suffix())
                    {
                        this->_emitTree(terminalsuffixed->terminalDef(),
                                        emitter, nodesList);
                    }
                    else
                    {
                        auto suffix_id = terminalsuffixed->atom_suffix()
                                             ->identifier()
                                             ->getText();
                        auto terminal_id =
                            terminalsuffixed->terminalDef()->getText();
                        emitter << terminal_id;
                        emitter << "{ ";
                        emitter << "astmap[$" << terminal_id
                                << ".ctx] = \"" << suffix_id << "\"; ";
                        if (!nodesList.empty() &&
                            std::count(nodesList.begin(), nodesList.end(),
                                       suffix_id))
                        {
                            emitter << "$ctxmap[\"" << suffix_id
                                    << "\"].push_back($" << terminal_id
                                    << ".ctx);";
                        }
                        emitter << " }";
                    }
                }
                else if (auto rulesuffixed = dynamic_cast<
                             UniASTParser::Suffixed_rulerefContext *>(
                             child))
                {
                    if (!rulesuffixed->atom_suffix())
                    {
                        this->_emitTree(rulesuffixed->ruleref(), emitter,
                                        nodesList);
                    }
                    else
                    {
                        auto suffix_id = rulesuffixed->atom_suffix()
                                             ->identifier()
                                             ->getText();
                        auto ruleref_id =
                            rulesuffixed->ruleref()->getText();
                        emitter << ruleref_id;
                        emitter << "{ ";
                        emitter << "astmap[$" << ruleref_id << ".ctx] = \""
                                << suffix_id << "\";";
                        if (!nodesList.empty() &&
                            std::count(nodesList.begin(), nodesList.end(),
                                       suffix_id))
                        {
                            emitter << "$ctxmap[\"" << suffix_id
                                    << "\"].push_back($" << ruleref_id
                                    << ".ctx);";
                        }
                        emitter << " } ";
                    }
                }
                else
                {
                    this->_emitTree(child, emitter, nodesList);
                }
            }
        }
    }

    void _collectNodesArgs(ExtraDecl &decl,
                           std::vector<std::string> &result)
    {
        if (decl.type == ExtraDecl::NODES)
        {
            result.push_back(decl.content);
        }
        else if (decl.type == ExtraDecl::CONSTRUCTOR)
        {
            for (auto &arg : decl.args)
            {
                this->_collectNodesArgs(arg, result);
            }
        }
        return;
    }

    void _emitExtraConstructDecls(ExtraDecl &decl, Emitter &emitter)
    {
        emitter << "\"" << decl.content
                << "\", std::vector<std::pair<std::string, std::any>>{";
        emitter.newLine();
        emitter.tab();
        emitter << "{";
        for (auto &arg : decl.args)
        {
            if (arg.type == ExtraDecl::TYPE)
            {
                emitter << "\"TYPE\", " << arg.content << "()";
            }
            else if (arg.type == ExtraDecl::CONSTRUCTOR)
            {
                emitter
                    << "\"CONSTRUCTOR\", std::pair<std::string, "
                       "std::vector<std::pair<std::string, std::any>>> {";
                emitter.newLine();
                emitter.tab();
                this->_emitExtraConstructDecls(arg, emitter);
                emitter.shiftTab();
                emitter << "}";
                emitter.newLine();
            }
            else if (arg.type == ExtraDecl::NODES)
            {
                emitter << "\"NODES\", $ctxmap[\"" << arg.content << "\"]";
            }
            else if (arg.type == ExtraDecl::STRING_LITERAL)
            {
                emitter << "\"STRING_LITERAL\", " << arg.content;
            }
        }
        emitter << "},";
        emitter.shiftTab();
        emitter << "}";
        emitter.newLine();
    }

    void _emitListener(std::vector<std::string> &ctxHasExtraDecl,
                       Emitter &headerEmitter, Emitter &cppEmitter)
    {
        // emit cpp
        cppEmitter << "#include \"" << this->grammarName << "Listener.h\"";
        cppEmitter.newLine();

        // emit header
        std::string upperedName = this->grammarName;
        std::transform(upperedName.begin(), upperedName.end(),
                       upperedName.begin(), ::toupper);
        headerEmitter << "#ifndef " << upperedName << "_LISTENER_H";
        headerEmitter.newLine();
        headerEmitter << "#define " << upperedName << "_LISTENER_H";
        headerEmitter.newLine();
        headerEmitter << "#include \"" << this->grammarName
                      << "ParserBaseListener.h\"";
        headerEmitter.newLine();
        headerEmitter << R"(#include "AST/UniASTNode.h")";
        headerEmitter.newLine();
        headerEmitter << "#include <any>";
        headerEmitter.newLine();
        headerEmitter << "#include <stack>";
        headerEmitter.newLine();
        headerEmitter.newLine();
        headerEmitter << "using namespace UniAST;";
        headerEmitter.newLine();
        // class defination
        headerEmitter << "class " << this->grammarName
                      << "Listener : public " << this->grammarName
                      << "ParserBaseListener {";
        headerEmitter.newLine();
        headerEmitter << R"(
private:
  std::map<antlr4::ParserRuleContext *, std::string> &astmap;
  RuleNode *root;
  std::stack<TreeNode *> nodeManager;

  RuleNode *_buildConstructTreeNode(
      std::pair<std::string, std::vector<std::pair<std::string, std::any>>>
          extraDecl) {
    auto extraDeclName = extraDecl.first;
    auto extraNode = new RuleNode(nullptr, extraDeclName);
    auto thisNode = this->nodeManager.top();
    for (auto &extraArg : extraDecl.second) {
      auto argType = extraArg.first;
      if (argType == "NODES") {
        auto args = std::any_cast<std::vector<antlr4::ParserRuleContext *>>(
            extraArg.second);
        for (auto ctx : args) {
          auto child = thisNode->findChild(ctx);
          thisNode->deleteChild(child);
          extraNode->addChild(child);
        }
        thisNode->addChild(extraNode);
      } else if (argType == "STRING_LITERAL") {
        std::cout << "TODO: argType == STRING_LITERAL " << std::endl;
      } else if (argType == "TYPE") {
        std::cout << "TODO: argType == TYPE " << std::endl;
      } else if (argType == "CONSTRUCTOR") {
        auto arg = std::any_cast<std::pair<
            std::string, std::vector<std::pair<std::string, std::any>>>>(
            extraArg.second);
        auto child = this->_buildConstructTreeNode(arg);
        thisNode->deleteChild(child);
        extraNode->addChild(child);
        thisNode->addChild(extraNode);
      } else {
        std::cerr << "unknown arg type: " << argType << std::endl;
        exit(1);
      }
    }
    return extraNode;
  }

public:
)";
        // constructor
        headerEmitter.tab();
        headerEmitter.newLine();
        headerEmitter << this->grammarName
                      << "Listener(std::map<antlr4::ParserRuleContext *, "
                         "std::string> &astmap)";
        headerEmitter.tab();
        headerEmitter.newLine();
        headerEmitter
            << ": astmap(astmap), root(new RuleNode(nullptr, \"root\")) {";
        headerEmitter.newLine();
        headerEmitter << "this->nodeManager.push(this->root);";
        headerEmitter.newLine();
        headerEmitter.shiftTab();
        headerEmitter << "};";
        headerEmitter.newLine();
        headerEmitter.shiftTab();
        headerEmitter.newLine();

        // destructor
        headerEmitter.tab();
        headerEmitter << "~" << this->grammarName << "Listener() { ";
        headerEmitter.newLine();
        headerEmitter.tab();
        headerEmitter << "delete root; ";
        headerEmitter.shiftTab();
        headerEmitter.newLine();
        headerEmitter << "}";
        headerEmitter.newLine();
        headerEmitter.shiftTab();
        headerEmitter.newLine();
        // other functions
        headerEmitter << R"(
  RuleNode *getRoot() { 
    return this->root; 
  }

  void printStringTree() {
    std::cout << this->root->toStringTree() << std::endl;
  }

  virtual void enterEveryRule(antlr4::ParserRuleContext *ctx) override {
    if (!this->astmap.contains(ctx)) {
      return;
    }
    auto nodeName = this->astmap[ctx];
    auto newNode = new RuleNode(ctx, nodeName);
    auto p = this->nodeManager.top();
    p->addChild(newNode);
    this->nodeManager.push(newNode);
  }
  virtual void exitEveryRule(antlr4::ParserRuleContext *ctx) override {
    if (!this->astmap.contains(ctx)) {
      return;
    }
    this->nodeManager.pop();
  }

)";

        for (auto ruleName : ctxHasExtraDecl)
        {
            auto upperedRuleName = ruleName;
            std::transform(upperedRuleName.begin(),
                           upperedRuleName.begin() + 1,
                           upperedRuleName.begin(), ::toupper);
            headerEmitter.tab();
            headerEmitter.newLine();
            headerEmitter << "virtual void exit" << upperedRuleName << "("
                          << this->grammarName
                          << "Parser::" << upperedRuleName
                          << "Context *ctx) override {";
            headerEmitter.newLine();
            headerEmitter.tab();
            headerEmitter
                << "for (auto construct : ctx->extra_node_construct) {";
            headerEmitter.newLine();
            headerEmitter.tab();
            headerEmitter << "this->_buildConstructTreeNode(construct);";
            headerEmitter.newLine();
            headerEmitter.shiftTab();
            headerEmitter << "}";
            headerEmitter.newLine();
            headerEmitter.shiftTab();
            headerEmitter << "}";
            headerEmitter.newLine();
            headerEmitter.shiftTab();
            headerEmitter.newLine();
        }

        headerEmitter << "};";
        headerEmitter.newLine();
        headerEmitter.newLine();
        headerEmitter << "#endif";
    }

    void _emitParserGrammarHead(Emitter &parserEmitter)
    {
        // head of parser grammar
        parserEmitter << "parser grammar " << grammarName << "Parser;";
        parserEmitter.newLine();
        parserEmitter << "options { tokenVocab = " << grammarName
                      << "Lexer; }";
        parserEmitter.newLine();
        parserEmitter.newLine();
        // emit heade files
        parserEmitter << "@header {";
        parserEmitter.newLine();
        parserEmitter << "#include <map>";
        parserEmitter.newLine();
        parserEmitter << "#include <string>";
        parserEmitter.newLine();
        parserEmitter << "#include <iostream>";
        parserEmitter.newLine();
        parserEmitter << "}";
        // emitter parser member
        parserEmitter.newLine();
        parserEmitter << "@parser::members {";
        parserEmitter.newLine();
        parserEmitter
            << "std::map<antlr4::ParserRuleContext*, std::string> astmap;";
        parserEmitter.newLine();
        parserEmitter << "}";
        parserEmitter.newLine();
    }

    void _emitLexerGrammarHead(Emitter &lexerEmitter)
    {
        // head of lexer grammar
        lexerEmitter << "lexer grammar " << grammarName << "Lexer;";
        lexerEmitter.newLine();
        lexerEmitter.newLine();
    }

    void _emitCMakeFile(Emitter &cmakeEmitter)
    {
        /*
        antlr_target(TestGrammarLexer TestGrammarLexer.g4
          LEXER
          )
        */
        cmakeEmitter << "antlr_target(" << this->grammarName << "Lexer "
                     << this->grammarName << "Lexer.g4";
        cmakeEmitter.newLine();
        cmakeEmitter.tab();
        cmakeEmitter << "LEXER";
        cmakeEmitter.newLine();
        cmakeEmitter << ")";
        cmakeEmitter.newLine();
        cmakeEmitter.shiftTab();
        cmakeEmitter.newLine();

        /*
          antlr_target(TestGrammarParser TestGrammarParser.g4
            DEPENDS_ANTLR
            TestGrammarLexer
            PARSER
            LISTENER
            VISITOR
            COMPILE_FLAGS -lib
            ${ANTLR_TestGrammarLexer_OUTPUT_DIR}
            )
        */
        cmakeEmitter << "antlr_target(" << this->grammarName << "Parser "
                     << this->grammarName << "Parser.g4";
        cmakeEmitter.newLine();
        cmakeEmitter.tab();
        cmakeEmitter << "DEPENDS_ANTLR";
        cmakeEmitter.newLine();
        cmakeEmitter << this->grammarName << "Lexer";
        cmakeEmitter.newLine();
        cmakeEmitter << "PARSER";
        cmakeEmitter.newLine();
        cmakeEmitter << "LISTENER";
        cmakeEmitter.newLine();
        cmakeEmitter << "VISITOR";
        cmakeEmitter.newLine();
        cmakeEmitter << "COMPILE_FLAGS -lib";
        cmakeEmitter.newLine();
        cmakeEmitter << "${ANTLR_" << this->grammarName
                     << "Lexer_OUTPUT_DIR}";
        cmakeEmitter.newLine();
        cmakeEmitter << ")";
        cmakeEmitter.newLine();
        cmakeEmitter.shiftTab();
        cmakeEmitter.newLine();

        /*
          include_directories(${ANTLR_TestGrammarLexer_OUTPUT_DIR})
        */
        cmakeEmitter << "include_directories(${ANTLR_" << this->grammarName
                     << "Lexer_OUTPUT_DIR}) ";
        cmakeEmitter.newLine();

        /*
          include_directories(${ANTLR_TestGrammarParser_OUTPUT_DIR})
        */
        cmakeEmitter << "include_directories(${ANTLR_" << this->grammarName
                     << "Parser_OUTPUT_DIR})";
        cmakeEmitter.newLine();
        cmakeEmitter.newLine();

        /*
        add_library(antlr_generated_TestGrammar
          ${ANTLR_TestGrammarLexer_CXX_OUTPUTS}
          ${ANTLR_TestGrammarParser_CXX_OUTPUTS}
          TestGrammarListener.cpp
          )
        */
        cmakeEmitter << "add_library(antlr_generated_"
                     << this->grammarName;
        cmakeEmitter.newLine();
        cmakeEmitter.tab();
        cmakeEmitter << "${ANTLR_" << this->grammarName
                     << "Lexer_CXX_OUTPUTS} ";
        cmakeEmitter.newLine();
        cmakeEmitter << "${ANTLR_" << this->grammarName
                     << "Parser_CXX_OUTPUTS}";
        cmakeEmitter.newLine();
        cmakeEmitter << this->grammarName << "Listener.cpp";
        cmakeEmitter.newLine();
        cmakeEmitter << ")";
        cmakeEmitter.newLine();
        cmakeEmitter.shiftTab();
        cmakeEmitter.newLine();

        /*
        include_directories(
          ${PROJECT_SOURCE_DIR}/runtime/include
          )
        */
        cmakeEmitter << "include_directories(";
        cmakeEmitter.newLine();
        cmakeEmitter.tab();
        cmakeEmitter << "${PROJECT_SOURCE_DIR}/runtime/include";
        cmakeEmitter.newLine();
        cmakeEmitter << ")";
        cmakeEmitter.shiftTab();
        cmakeEmitter.newLine();
        cmakeEmitter.newLine();
        /*
          target_link_libraries(
            antlr_generated_TestGrammar
            PUBLIC
            antlr4_static
            )
        */
        cmakeEmitter << "target_link_libraries(";
        cmakeEmitter.newLine();
        cmakeEmitter.tab();
        cmakeEmitter << "antlr_generated_" << this->grammarName;
        cmakeEmitter.newLine();
        cmakeEmitter << "PUBLIC";
        cmakeEmitter.newLine();
        cmakeEmitter << "antlr4_static";
        cmakeEmitter.newLine();
        cmakeEmitter << ")";
        cmakeEmitter.shiftTab();
        cmakeEmitter.newLine();
        cmakeEmitter.newLine();
        // target driver
        /*
          add_executable(
            TestGrammar_exe
            Driver.cpp
          )
        */
        cmakeEmitter << "add_executable(";
        cmakeEmitter.newLine();
        cmakeEmitter.tab();
        cmakeEmitter << this->grammarName << "_exe";
        cmakeEmitter.newLine();
        cmakeEmitter << "Driver.cpp";
        cmakeEmitter.newLine();
        cmakeEmitter.shiftTab();
        cmakeEmitter << ")";
        cmakeEmitter.newLine();
        cmakeEmitter.newLine();
        /*
          target_link_libraries(TestGrammar_exe
            PRIVATE
            antlr_generated_TestGrammar
            basicvisitor
            logger
            uniastnode
          )
        */
        cmakeEmitter << "target_link_libraries(" << this->grammarName
                     << "_exe";
        cmakeEmitter.newLine();
        cmakeEmitter.tab();
        cmakeEmitter << "PRIVATE";
        cmakeEmitter.newLine();
        cmakeEmitter << "antlr_generated_" << this->grammarName;
        cmakeEmitter.newLine();
        cmakeEmitter << "basicvisitor";
        cmakeEmitter.newLine();
        cmakeEmitter << "logger";
        cmakeEmitter.newLine();
        cmakeEmitter << "uniastnode";
        cmakeEmitter.newLine();
        cmakeEmitter << ")";
        cmakeEmitter.shiftTab();
        cmakeEmitter.newLine();
        cmakeEmitter.newLine();
    }

    void _emitDriverCppFile(Emitter &driverEmitter)
    {
        driverEmitter << "#include \"TestGrammarLexer.h\"";
        driverEmitter.newLine();
        driverEmitter << "#include \"TestGrammarListener.h\"";
        driverEmitter.newLine();
        driverEmitter << "#include \"TestGrammarParser.h\"";
        driverEmitter.newLine();
        driverEmitter << R"(
#include "visitor/BasicVisitor.h"
#include <tree/ParseTreeWalker.h>

using namespace antlr4;
using namespace std;
using namespace UniAST;
)";
        driverEmitter << "int main(int argc, char **argv) {";
        driverEmitter.tab();
        driverEmitter.newLine();
        driverEmitter << "std::ifstream stream;";
        driverEmitter.newLine();
        driverEmitter << "stream.open(argv[1]);";
        driverEmitter.newLine();
        driverEmitter << "ANTLRInputStream input(stream);";
        driverEmitter.newLine();
        driverEmitter << this->grammarName << "Lexer lexer(&input);";
        driverEmitter.newLine();
        driverEmitter << "CommonTokenStream tokens(&lexer);";
        driverEmitter.newLine();
        driverEmitter << this->grammarName << "Parser parser(&tokens);";
        driverEmitter.newLine();
        driverEmitter << "auto root = parser.module();";
        driverEmitter.newLine();
        driverEmitter << this->grammarName
                      << "Listener listener(parser.astmap);";
        driverEmitter.newLine();
        driverEmitter << "tree::ParseTreeWalker walker;";
        driverEmitter.newLine();
        driverEmitter << "walker.walk(&listener, root);";
        driverEmitter.newLine();
        driverEmitter << "listener.printStringTree();";
        driverEmitter.newLine();
        driverEmitter << "BasicVisitor visitor;";
        driverEmitter.newLine();
        driverEmitter << "visitor.visit(listener.getRoot());";
        driverEmitter.newLine();
        driverEmitter << "return 0;";
        driverEmitter.newLine();
        driverEmitter << "}";
        driverEmitter.shiftTab();
        driverEmitter.newLine();
    }

  public:
    Visitor(std::string grammarName,
            std::ostream &lexerFilestream = std::cout,
            std::ostream &parserFileStream = std::cout,
            std::ostream &listenerHeaderStream = std::cout,
            std::ostream &listenerCppStream = std::cout,
            std::ostream &cmakeStream = std::cout,
            std::ostream &driverStream = std::cout)
        : lexerEmitter(Emitter(lexerFilestream)),
          parserEmitter(Emitter(parserFileStream)),
          listenerHeaderEmitter(listenerHeaderStream),
          listenerCppEmitter(listenerCppStream), cmakeEmitter(cmakeStream),
          driverEmitter(driverStream), grammarName(grammarName),
          ctxHasExtraDecl({})
    {
        _emitParserGrammarHead(this->parserEmitter);
        _emitLexerGrammarHead(this->lexerEmitter);
        _emitCMakeFile(this->cmakeEmitter);
        _emitDriverCppFile(this->driverEmitter);
    }

    ~Visitor()
    {
        // ignore white space and newline
        this->lexerEmitter << "Whitespace: [ \\t]+ -> skip;";
        this->lexerEmitter.newLine();
        this->lexerEmitter.newLine();
        this->lexerEmitter << "Newline: ('\\r' '\\n'? | '\\n') -> skip;";
        // emit listener header and src
        this->_emitListener(this->ctxHasExtraDecl,
                            this->listenerHeaderEmitter,
                            this->listenerCppEmitter);
    }

    virtual std::any
    visitParserRuleSpec(UniASTParser::ParserRuleSpecContext *ctx) override
    {
        // emit rule name
        auto ruleName = ctx->ParserRuleName()->getText();
        this->parserEmitter << ruleName;
        this->parserEmitter.newLine();
        this->parserEmitter.tab();
        std::vector<std::string> nodesList;
        if (ctx->extra_decls())
        {
            this->ctxHasExtraDecl.push_back(ruleName);
            // this->parserEmitter locals
            this->parserEmitter << "locals[";
            this->parserEmitter.newLine();
            this->parserEmitter.tab();
            this->parserEmitter
                << "std::map<std::string, "
                   "std::vector<antlr4::ParserRuleContext*>> ctxmap, ";
            this->parserEmitter.newLine();
            this->parserEmitter
                << "std::map<std::string, "
                   "std::vector<std::pair<std::string, std::any>>> "
                   "extra_node_construct";
            this->parserEmitter.newLine();
            this->parserEmitter.shiftTab();
            this->parserEmitter << "]";
            this->parserEmitter.newLine();
            auto extra_decls = std::any_cast<std::vector<ExtraDecl>>(
                this->visit(ctx->extra_decls()));

            // emit init
            // collect nodes list to init ctxmap
            for (auto decl : extra_decls)
            {
                for (auto arg : decl.args)
                {
                    this->_collectNodesArgs(arg, nodesList);
                }
            }
            this->parserEmitter << "@init {";
            this->parserEmitter.newLine();
            this->parserEmitter.tab();
            this->parserEmitter << "$ctxmap = {";
            this->parserEmitter.newLine();
            this->parserEmitter.tab();
            for (size_t i = 0; i < nodesList.size(); i++)
            {
                this->parserEmitter
                    << "std::make_pair(\"" << nodesList[i]
                    << "\", std::vector<antlr4::ParserRuleContext*>{}), ";
                this->parserEmitter.newLine();
            }
            this->parserEmitter.shiftTab();
            this->parserEmitter << "};";
            this->parserEmitter.shiftTab();
            this->parserEmitter.newLine();
            this->parserEmitter << "}";
            this->parserEmitter.newLine();
            // emit after
            this->parserEmitter << "@after {";
            this->parserEmitter.newLine();
            this->parserEmitter.tab();
            this->parserEmitter << "$extra_node_construct = {";
            this->parserEmitter.newLine();
            this->parserEmitter.tab();
            for (auto decl : extra_decls)
            {
                this->parserEmitter << "{";
                this->_emitExtraConstructDecls(decl, this->parserEmitter);
                this->parserEmitter << "}, ";
            }
            this->parserEmitter.shiftTab();
            this->parserEmitter << "};";
            this->parserEmitter.newLine();
            this->parserEmitter.shiftTab();
            this->parserEmitter << "}";
            this->parserEmitter.newLine();
            this->parserEmitter.shiftTab();
        }

        this->parserEmitter.shiftTab();
        this->parserEmitter.tab();
        this->parserEmitter << ": ";
        this->_emitTree(ctx->ruleBlock(), this->parserEmitter, nodesList);
        this->parserEmitter.newLine();
        this->parserEmitter << ";";
        this->parserEmitter.shiftTab();
        this->parserEmitter.newLine();
        this->parserEmitter.newLine();
        return std::any();
    }

    virtual std::any
    visitLexerRuleSpec(UniASTParser::LexerRuleSpecContext *ctx) override
    {
        // emit rule name
        auto ruleName = ctx->LexerRuleName()->getText();
        this->lexerEmitter << ruleName;
        this->lexerEmitter.newLine();
        this->lexerEmitter.tab();
        this->lexerEmitter << ": ";
        this->_emitTree(ctx->lexerRuleBlock(), this->lexerEmitter);
        this->lexerEmitter.newLine();
        this->lexerEmitter << ";";
        this->lexerEmitter.shiftTab();
        this->lexerEmitter.newLine();
        this->lexerEmitter.newLine();
        return std::any();
    }

    virtual std::any
    visitExtra_decls(UniASTParser::Extra_declsContext *ctx) override
    {
        std::vector<ExtraDecl> decls;
        for (auto declctx : ctx->extra_decl())
        {
            auto decl = this->visit(declctx);
            decls.push_back(std::any_cast<ExtraDecl>(decl));
        }
        return decls;
    }

    virtual std::any
    visitExtra_decl(UniASTParser::Extra_declContext *ctx) override
    {
        ExtraDecl decl;
        decl.type = ExtraDecl::CONSTRUCTOR;
        decl.content = ctx->extraNodeName()->getText();
        decl.args = std::vector<ExtraDecl>{std::any_cast<ExtraDecl>(
            this->visit(ctx->extraNodeRightValue()))};
        return decl;
    }

    virtual std::any visitExtraNodeRightValue(
        UniASTParser::ExtraNodeRightValueContext *ctx) override
    {
        if (ctx->extraNodeConstructure())
        {
            return this->visit(ctx->extraNodeConstructure());
        }
        else if (ctx->identifier())
        {
            auto name = ctx->identifier()->getText();
            ExtraDecl decl;
            std::set<std::string> typeset = {"any", "int", "float",
                                             "string"};
            if (typeset.contains(name))
            {
                decl.type = ExtraDecl::TYPE;
            }
            else
            {
                decl.type = ExtraDecl::NODES;
            }
            decl.content = name;
            return decl;
        }
        else
        {
            auto literal = ctx->identifier()->getText();
            ExtraDecl decl;
            decl.content = literal;
            decl.type = ExtraDecl::STRING_LITERAL;
            return decl;
        }
    }

    virtual std::any visitExtraNodeConstructure(
        UniASTParser::ExtraNodeConstructureContext *ctx) override
    {
        ExtraDecl decl;
        decl.type = ExtraDecl::CONSTRUCTOR;
        decl.content = ctx->extraNodeConstructureName()->getText();
        for (auto argctx : ctx->extraNodeRightValue())
        {
            decl.args.push_back(
                std::any_cast<ExtraDecl>(this->visit(argctx)));
        }
        return decl;
    }
};

} // namespace UniAST

#endif